

#ifndef P2P_PEER_HPP
#define P2P_PEER_HPP

#include "Common.hpp"
#include "Session.hpp"
#include "../muduo/base/Timestamp.h"
#include "../muduo/net/TcpClient.h"
#include "../muduo/net/EventLoop.h"
#include "../muduo/net/TcpConnection.h"
#include <memory>
#include <vector>
#include <spdlog/spdlog.h>


/**
 * @brief Representation of connectivity state and all other pertinent Peer metadata.
 * A Peer represents connectivity between two nodes, which in this case, are the host
 * and remote nodes.
 *
 * State information necessary for loading network topology is maintained by NodeTable.
 */

class Peer:public Node, public std::enable_shared_from_this<Peer> 
{
    friend class Session;   // Allows Session to update score and rating.
    friend class Host;      // For Host: saveNetwork(), restoreNetwork()
    friend class Handshake;
public: 
    Peer(Node const& node,bool outbound,bool persistent, Host* host,
            muduo::net::EventLoop *loop,std::shared_ptr<Session> m_session,
            std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("peer")):
        Node(node),outbound_(outbound),persistent_(persistent),host_(host),loop_(loop),m_session_(m_session),logger_(logger)
        {
            if(outbound_)
            {
                client_=std::unique_ptr<muduo::net::TcpClient>(new muduo::net::TcpClient(loop_,this->addr_ ,this->addr_.toIpPort()));
                client_->setConnectionCallback([this](const muduo::net::TcpConnectionPtr& conn)
                {
                    m_session_ = std::make_shared<Session> (this->host_,this->loop_,shared_from_this());
                });
                client_->setConnectionFailCallback([this](muduo::Timestamp ti){
                    this->logger_->error("Connect to the peer {} fail",this->addr_.toIpPort());
                    this->m_failedAttemps.fetch_add(1);
                    this->m_lastAttempted = ti;
                });
                client_->connect();
            }
        }

    bool isOutbound() const { return outbound_; }

    /// Return true if connection attempt should be made to this peer or false if
    bool isPersistent() const { return persistent_; }

	/// Number of times connection has been attempted to peer.
    int failedAttempts() const { return m_failedAttemps; }

    
    // Peer session is noted as useful
    void noteSessionGood(){ m_failedAttemps = 0; }

    void send(unsigned char chID,const bytes &data);

    std::string toString();
protected:

    Host * host_;
    //Network Availability
    bool outbound_;
    bool persistent_;

    muduo::net::EventLoop *loop_;

    std::unique_ptr<muduo::net::TcpClient> client_;
    std::shared_ptr<Session> m_session_;


    std::shared_ptr<spdlog::logger> logger_;
    muduo::Timestamp m_lastConnected;
    muduo::Timestamp m_lastAttempted;
    std::atomic<unsigned> m_failedAttemps{0};
};

struct peerSetWithLock
{
    std::vector<Peer> peers;
    std::mutex mutex;
};

#endif
